/*
 * @Author       : Bing Chen chenbing@iocharger.com
 * @Date         : 2024-05-21 09:08:29
 * @LastEditors  : Bing Chen chenbing@iocharger.com
 * @LastEditTime : 2025-03-15 12:52:30
 * @FilePath     : \openchaoji-corelib\secc\gbt27930\OpenChaoJi_secc_gbt27930_system.c
 * @Description  : 本文件实现secc系统状态机切换的基本逻辑
 *
 * Copyright (c) 2025 OpenChaoJi
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the icense for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include "../include/OpenChaoJi_secc.h"

const struct OpenChaoJi_secc_module * OpenChaoJi_secc_service_find_module(struct OpenChaoJi_secc_gbt27930_system * secc_gbt27930_system, const int module_status_id)
{
    const struct OpenChaoJi_secc_module * module = NULL;

    int module_id = OPENCHAOJI_MODULE_OF_ID(module_status_id);

    for (int i = 0; i < secc_gbt27930_system->module_array_len; i++)
    {
        module = secc_gbt27930_system->module_array[i];

        if ((module != NULL) && (module->module_id == module_id))
        {
            return module;
        }
    }

    return NULL;
}

#if (OPENCHAOJI_CONFIG_ENABLE_GBT27930_B == CONFIG_YES)
const struct OpenChaoJi_secc_module * OpenChaoJi_secc_service_find_module_by_fdc(struct OpenChaoJi_secc_gbt27930_system * secc_gbt27930_system, enum OpenChaoJi_gbt27930_b_FCType fc_type, uint8_t fdc_type)
{
    const struct OpenChaoJi_secc_module * module = NULL;

    for (int i = 0; i < secc_gbt27930_system->module_array_len; i++)
    {
        module = secc_gbt27930_system->module_array[i];

        if ((module != NULL) && (module->fc_type == fc_type) && (module->fdc_type == fdc_type))
        {
            return module;
        }
    }

    return NULL;
}
#endif

/**
 * @brief: 根据一个全局的唯一状态id, 找到该系统的对应的状态处理机
 * @param {OpenChaoJi_secc_gbt27930_system *} secc_gbt27930_system 系统定义
 * @param {int} module_status_id 全局唯一状态id
 * @return {*} 该id对应的状态处理机
 */
const struct OpenChaoJi_secc_module_status * OpenChaoJi_secc_service_find_status(struct OpenChaoJi_secc_gbt27930_system * secc_gbt27930_system, const int module_status_id)
{
    const struct OpenChaoJi_secc_module * module = NULL;

    int module_id = OPENCHAOJI_MODULE_OF_ID(module_status_id);

    for (int i = 0; i < secc_gbt27930_system->module_array_len; i++)
    {
        module = secc_gbt27930_system->module_array[i];

        if ((module == NULL) || (module->module_id != module_id))
        {
            continue;
        }

        for (int j = 0; j < module->status_array_len; j++)
        {
            if ((module->status_array + j)->module_status_id == module_status_id)
            {
                return (module->status_array + j);
            }
        }
    }

    return NULL;
}


const char * OpenChaoJi_secc_module_name_func(struct OpenChaoJi_secc_context * context, int module_status_id)
{
    const struct OpenChaoJi_secc_module * module = NULL;

    int module_id = OPENCHAOJI_MODULE_OF_ID(module_status_id);

    for (int i = 0; i < context->secc_gbt27930_system->module_array_len; i++)
    {
        module = context->secc_gbt27930_system->module_array[i];

        if ((module != NULL) && (module->module_id == module_id))
        {
            if (module->name != NULL)
            {
                return module->name;
            }
            else
            {
                return "UndefinedModuleName";
            }
        }
    }

    return "UnknownModuleName";
}

const char * OpenChaoJi_secc_status_name_func(struct OpenChaoJi_secc_context * context, int module_status_id)
{
    const struct OpenChaoJi_secc_module * module = NULL;

    int module_id = OPENCHAOJI_MODULE_OF_ID(module_status_id);

    for (int i = 0; i < context->secc_gbt27930_system->module_array_len; i++)
    {
        module = context->secc_gbt27930_system->module_array[i];

        if ((module == NULL) || (module->module_id != module_id))
        {
            continue;
        }

        for (int j = 0; j < module->status_array_len; j++)
        {
            if ((module->status_array + j)->module_status_id == module_status_id)
            {
                if ((module->status_array + j)->name != NULL)
                {
                    return (module->status_array + j)->name;
                }
                else
                {
                    return "UndefinedStatusName";
                }
            }
        }
    }

    return "UnknownStatusName";
}


/**
 * @brief: 状态切换处理, 需要执行旧状态的post方法, 再执行新状态的prev方法
 * @param {OpenChaoJi_secc_context *} context 上下文
 * @param {int} next_status_id 下一个状态
 * @return {*} 无
 */
static void OpenChaoJi_secc_service_switch_status(struct OpenChaoJi_secc_context * context, int next_status_id)
{
    /* 只有状态发生变化才进行切换处理 */
    if (context->current_module_status_id != next_status_id)
    {
        const struct OpenChaoJi_secc_module_status * old_status = OpenChaoJi_secc_service_find_status(context->secc_gbt27930_system, context->current_module_status_id);
        const struct OpenChaoJi_secc_module_status * next_status = OpenChaoJi_secc_service_find_status(context->secc_gbt27930_system, next_status_id);
		LOG_STATUS_CHANGE("[%d]Switch %s[%08X]->%s[%08X]", context->tag
            , old_status->name, context->current_module_status_id
            , next_status->name, next_status_id);
        /* 离开旧状态 */
        if (old_status->post_func != NULL)
        {
            old_status->post_func(context);
        }

        /* 进入新状态 */
        if (next_status->prev_func != NULL)
        {
            next_status->prev_func(context);
        }

        /* 记录时间戳, 可能用于各种状态超时判断处理 */
        context->current_status_ticks = OpenChaoJi_os_get_ticks();
        context->current_module_status_id = next_status_id;
    }

    return;
}

/**
 * @brief: 当前系统的状态机主处理方法, 需要定时触发, 一般建议不超过20ms调用一次
 *         注意, 由于兼容非系统方式实现, 所以本项目所有方法默认处于同一线程, 如厂家通过操作系统实现, 厂家需要保证线程安全
 * @param {OpenChaoJi_secc_context *} context
 * @return {*}
 */
void OpenChaoJi_secc_service(struct OpenChaoJi_secc_context * context)
{
    int next_status_id = 0;

    /* 会话层有独立的service驱动, 用于驱动导引和传输层 */
    OpenChaoJi_secc_utility_session_service(context->context_session);

    /* 先查找到当前状态表 */
    const struct OpenChaoJi_secc_module_status * status = OpenChaoJi_secc_service_find_status(context->secc_gbt27930_system, context->current_module_status_id);

    /* 执行当前状态表相关操作, 确认是否需要切换状态 */
    if (status != NULL)
    {
        /* 先从对端读取数据, 同步对端状态 */
        if (context->secc_gbt27930_system->data_read_func != NULL)
        {
            context->secc_gbt27930_system->data_read_func(context);
        }

        /* 先执行通用子过程, 进行必要的检查判断, 特别是异常处理 */
        if (context->secc_gbt27930_system->sub_module_func != NULL)
        {
            next_status_id = context->secc_gbt27930_system->sub_module_func(context);

            /* 子过程未引起状态跳转, 则执行当前状态的过程方法, 确认是否可能发生状态切换 */
            if (next_status_id == context->current_module_status_id)
            {
                next_status_id = status->loop_func(context);
            }
        }
        if (next_status_id != context->current_module_status_id)
        {
            OpenChaoJi_secc_service_switch_status(context, next_status_id);
        }

        /* 最后发送本方数据到对端, 同步本方状态给对端 */
        if (context->secc_gbt27930_system->data_write_func != NULL)
        {
            context->secc_gbt27930_system->data_write_func(context);
        }
    }
    else
    {
        /* 正常来说状态不可能为空, 但是防止编码赋值错误, 这里需要处理空的情况, 需要把系统置为故障状态 */
        LOG_ALERT("[%d] Unknown Status",context->tag);
    }
}


// 初始化上下文, 每一把枪注册一个上下文, 所以本项目支持不同类型系统混合使用
struct OpenChaoJi_secc_context * OpenChaoJi_secc_create_context(const struct OpenChaoJi_secc_context_create_info * info)
{
    struct OpenChaoJi_secc_context * context = OpenChaoJi_os_malloc(sizeof(struct OpenChaoJi_secc_context));
    if (context == NULL)
    {
        return NULL;
    }
    memset(context, 0x00, sizeof(struct OpenChaoJi_secc_context));

    /* 记录hal参数, 供HAL层内部使用 */
    context->hal_handle = info->hal_handle;
    context->tag = info->tag;

    /* 初始化Utiltiy各层 */
    context->context_pilot = OpenChaoJi_secc_utility_pilot_init(&info->pilot);
    if (context->context_pilot == NULL)
    {
        goto FAILED_CREATE;
    }

    context->context_session = OpenChaoJi_secc_utility_session_init(&info->session);
    if (context->context_session == NULL)
    {
        goto FAILED_CREATE;
    }

    /* 初始化System */
    context->secc_gbt27930_type = info->secc_gbt27930_type;
#if (OPENCHAOJI_CONFIG_ENABLE_GBT27930_A == CONFIG_YES)
    if (info->secc_gbt27930_type == OPENCHAOJI_SECC_GBT27930_TYPE_A)
    {
       if (OpenChaoJi_secc_create_system_gbt27930_a(context) != 0)
       {
           goto FAILED_CREATE;
       }
    }
#endif
#if (OPENCHAOJI_CONFIG_ENABLE_GBT27930_B == CONFIG_YES)
    if (info->secc_gbt27930_type == OPENCHAOJI_SECC_GBT27930_TYPE_B)
    {
        if (OpenChaoJi_secc_create_system_gbt27930_b(context) != 0)
        {
            goto FAILED_CREATE;
        }
    }
#endif

    LOG_NOTICE("[%d]Create Context SUCCESS", context->tag);
    return context;

FAILED_CREATE:
    LOG_ALERT("[%d]Create Context FAILED", info->tag);

    OpenChaoJi_secc_destroy_context(context);

    return NULL;
}

// 销毁一个上下文
void OpenChaoJi_secc_destroy_context(struct OpenChaoJi_secc_context * context)
{
    if (context == NULL)
    {
        return;
    }

#if (OPENCHAOJI_CONFIG_ENABLE_GBT27930_A == CONFIG_YES)
    if (context->secc_gbt27930_type == OPENCHAOJI_SECC_GBT27930_TYPE_A)
    {
       OpenChaoJi_secc_destory_system_gbt27930_a(context);
    }
#endif
    #if (OPENCHAOJI_CONFIG_ENABLE_GBT27930_B == CONFIG_YES)
    else if (context->secc_gbt27930_type == OPENCHAOJI_SECC_GBT27930_TYPE_B)
    {
        OpenChaoJi_secc_destory_system_gbt27930_b(context);
    }
#endif
    if (context->context_pilot != NULL)
    {
        OpenChaoJi_secc_utility_pilot_term(context->context_pilot);
    }
    if (context->context_session != NULL)
    {
        OpenChaoJi_secc_utility_session_term(context->context_session);
    }

    OpenChaoJi_os_free(context);


}
